// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Collections.Generic;
using System.Reflection.Metadata;

using Internal;
using Internal.TypeSystem;

using TypeHashingAlgorithms = Internal.NativeFormat.TypeHashingAlgorithms;
using Interlocked = System.Threading.Interlocked;
using Debug = System.Diagnostics.Debug;

namespace ILCompiler
{
    public partial class CompilerTypeSystemContext
    {
        private ModuleDesc _generatedAssembly;

        public ModuleDesc GeneratedAssembly
        {
            get
            {
                if (_generatedAssembly == null)
                {
                    Interlocked.CompareExchange(ref _generatedAssembly, new CompilerGeneratedAssembly(this), null);
                }

                return _generatedAssembly;
            }
        }

        private sealed class CompilerGeneratedAssembly : ModuleDesc, IAssemblyDesc
        {
            private MetadataType _globalModuleType;

            public override IAssemblyDesc Assembly => this;

            public ReadOnlySpan<byte> Name => "System.Private.CompilerGenerated"u8;

            public CompilerGeneratedAssembly(TypeSystemContext context)
                : base(context, null)
            {
                _globalModuleType = new CompilerGeneratedType(this);
            }

            public override IEnumerable<MetadataType> GetAllTypes()
            {
                return Array.Empty<MetadataType>();
            }

            public override MetadataType GetGlobalModuleType()
            {
                return _globalModuleType;
            }

            public AssemblyNameInfo GetName()
            {
                return new AssemblyNameInfo("System.Private.CompilerGenerated");
            }

            public override object GetType(ReadOnlySpan<byte> nameSpace, ReadOnlySpan<byte> name, NotFoundBehavior notFoundBehavior)
            {
                Debug.Fail("Resolving a TypeRef in the compiler generated assembly?");
                throw new NotImplementedException();
            }
        }

        /// <summary>
        /// A pseudo-type that owns helper methods generated by the compiler.
        /// This type should never be allocated (we should never see an MethodTable for it).
        /// </summary>
        internal sealed partial class CompilerGeneratedType : MetadataType
        {
            private int _hashcode;

            public CompilerGeneratedType(ModuleDesc module)
            {
                Module = module;
            }

            public override TypeSystemContext Context
            {
                get
                {
                    return Module.Context;
                }
            }

            public override ReadOnlySpan<byte> Name
            {
                get
                {
                    return "<Module>"u8;
                }
            }

            public override string DiagnosticName
            {
                get
                {
                    return "<Module>";
                }
            }

            public override ReadOnlySpan<byte> Namespace
            {
                get
                {
                    return "Internal.CompilerGenerated"u8;
                }
            }

            public override string DiagnosticNamespace
            {
                get
                {
                    return "Internal.CompilerGenerated";
                }
            }

            public override int GetHashCode()
            {
                if (_hashcode != 0)
                    return _hashcode;
                return InitializeHashCode();
            }

            private int InitializeHashCode()
            {
                return _hashcode = VersionResilientHashCode.NameHashCode(Namespace, Name);
            }

            public override bool IsCanonicalSubtype(CanonicalFormKind policy)
            {
                Debug.Assert(!HasInstantiation, "Why is this generic?");
                return false;
            }

            protected override TypeFlags ComputeTypeFlags(TypeFlags mask)
            {
                return TypeFlags.Class |
                    TypeFlags.HasGenericVarianceComputed |
                    TypeFlags.HasStaticConstructorComputed |
                    TypeFlags.HasFinalizerComputed |
                    TypeFlags.AttributeCacheComputed;
            }

            public override ClassLayoutMetadata GetClassLayout()
            {
                return default;
            }

            public override bool HasCustomAttribute(string attributeNamespace, string attributeName)
            {
                return false;
            }

            public override IEnumerable<MetadataType> GetNestedTypes()
            {
                return Array.Empty<MetadataType>();
            }

            public override MetadataType GetNestedType(string name)
            {
                return null;
            }

            protected override MethodImplRecord[] ComputeVirtualMethodImplsForType()
            {
                return Array.Empty<MethodImplRecord>();
            }

            public override MethodImplRecord[] FindMethodsImplWithMatchingDeclName(ReadOnlySpan<byte> name)
            {
                return Array.Empty<MethodImplRecord>();
            }

            public override ModuleDesc Module
            {
                get;
            }

            public override PInvokeStringFormat PInvokeStringFormat
            {
                get
                {
                    return PInvokeStringFormat.AutoClass;
                }
            }

            public override bool IsExplicitLayout
            {
                get
                {
                    return false;
                }
            }

            public override bool IsSequentialLayout
            {
                get
                {
                    return false;
                }
            }

            public override bool IsExtendedLayout
            {
                get
                {
                    return false;
                }
            }

            public override bool IsAutoLayout
            {
                get
                {
                    return true;
                }
            }

            public override bool IsBeforeFieldInit
            {
                get
                {
                    return false;
                }
            }

            public override MetadataType BaseType
            {
                get
                {
                    // Since this type should never be allocated and only serves the purpose of grouping things,
                    // it can act like a <Module> type and have no base type.
                    return null;
                }
            }

            public override bool IsSealed
            {
                get
                {
                    return true;
                }
            }

            public override bool IsAbstract
            {
                get
                {
                    return false;
                }
            }

            public override MetadataType ContainingType
            {
                get
                {
                    return null;
                }
            }

            public override DefType[] ExplicitlyImplementedInterfaces
            {
                get
                {
                    return Array.Empty<DefType>();
                }
            }
        }
    }
}
